#pragma once
#include <iostream>
#include <unistd.h>
#include <queue>
#include <pthread.h>

namespace ns_tp
{
    const int default_num = 5;

    template <class T>
    class ThreadPool
    {
    public:
        static ThreadPool<T>* Getsingle(int num = default_num)
        {
            if (_self == nullptr)
            {
                _self = new ThreadPool(num);
                return _self;
            }
            return _self;
        }

    private:
        ThreadPool(int thread_num = default_num)
            : _thread_num(thread_num)
        {
            pthread_mutex_init(&_mtx, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }

        ThreadPool(ThreadPool<T>& tp) = delete;
        ThreadPool<T>& operator=(ThreadPool<T>& tp) = delete;
    public:
        void ThreadPoolInit();
        static void *service_run(void *args);
        void Push(const T &sock);
        void Pop(T *sock);
        ~ThreadPool()
        {
            pthread_mutex_destroy(&_mtx);  
            pthread_cond_destroy(&_cond);
        }

    private:
        
        int _thread_num;
        std::queue<T> _task_queue;
        pthread_mutex_t _mtx;
        pthread_cond_t _cond;
        static ThreadPool<T>* _self;
    };

    template<class T>
    ThreadPool<T>* ThreadPool<T>::_self = nullptr;

    template <class T>
    void *ThreadPool<T>::service_run(void *args)
    {
        ThreadPool<T> *self = (ThreadPool<T> *)args;
        T sock;
        self->Pop(&sock);
        std::cout << "线程[" << pthread_self() << "]为你服务" << std::endl;
        sock();
    }

    template <class T>
    void ThreadPool<T>::ThreadPoolInit()
    {
        pthread_t tid;
        for (int i = 0; i < _thread_num; i++)
        {
            pthread_create(&tid, nullptr, service_run, (void *)this);
        }
    }

    template <class T>
    void ThreadPool<T>::Push(const T &in)
    {
        pthread_mutex_lock(&_mtx);
        _task_queue.push(in);
        pthread_cond_signal(&(this->_cond));
        pthread_mutex_unlock(&_mtx);
    }

    template <class T>
    void ThreadPool<T>::Pop(T *out)
    {
        pthread_mutex_lock(&_mtx);
        while (_task_queue.empty())
        {
            pthread_cond_wait(&_cond, &_mtx);
        }
        *out = _task_queue.front();
        _task_queue.pop();
        pthread_mutex_unlock(&_mtx);
    }
}
